package com.bridgeintelligent.tag.webserver.dim.mgmt.service.export.excel;

import com.bridgeintelligent.tag.constants.DimCofig;
import com.bridgeintelligent.tag.mapper.*;
import com.bridgeintelligent.tag.webserver.dim.mgmt.api.*;
import com.bridgeintelligent.tag.webserver.dim.mgmt.api.export.DimAggData;
import com.bridgeintelligent.tag.webserver.dim.mgmt.api.export.DimCompareData;
import com.bridgeintelligent.tag.webserver.dim.mgmt.api.export.DimCompareData.ContrastModel;
import com.bridgeintelligent.tag.webserver.dim.mgmt.api.export.ExcelCatalogModel;
import com.bridgeintelligent.tag.webserver.dim.mgmt.api.export.ExcelDimDataModel;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.wayneleo.quickstart.framework.ExceptionCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author JackCheung
 * @date 2020/7/14 5:33 PM.
 * description:
 */
@Service
@Slf4j
public class DimExcelMgmtServiceImpl implements IDimExcelMgmtService {

    private DimAggMapper dimAggMapper;
    private DimExcelMgmtMapper dimExcelMgmtMapper;
    private CatalogMapper catalogMapper;
    @Value("${nas.remoteStorage}")
    String remoteStorage;
    private SqlSessionTemplate sqlSessionTemplate;
    @Autowired
    private DepartmentMapper departmentMapper;

    public DimExcelMgmtServiceImpl(DimAggMapper dimAggMapper, DimExcelMgmtMapper dimExcelMgmtMapper, CatalogMapper catalogMapper, SqlSessionTemplate sqlSessionTemplate) {
        this.dimAggMapper = dimAggMapper;
        this.dimExcelMgmtMapper = dimExcelMgmtMapper;
        this.catalogMapper = catalogMapper;
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    /**
     * 下载上次上传的模版
     *
     * @param response HttpServletResponse
     */
    @SuppressWarnings("ResultOfMethodCallIgnored")
    @Override
    public void downLoad(HttpServletResponse response, HttpServletRequest request) {
        ByteArrayInputStream bis = null;
        OutputStream os = null;
        byte[] data;
        try {
            response.setContentType("text/plain;charset=UTF-8");
            String fileName = FILE_FULL_NAME;
            boolean flag = request.getHeader("User-Agent")
                    .indexOf("like Gecko") > 0;
            if (request.getHeader("User-Agent")
                    .toLowerCase()
                    .indexOf("msie") > 0 || flag) {
                fileName = URLEncoder.encode(fileName, "UTF-8");//IE浏览器
            } else {

                fileName = new String(fileName.replaceAll(" ", "")
                        .getBytes("UTF-8"), "ISO8859-1");
            }
            response.setHeader("Content-Disposition", "attachment;fileName=" + fileName);
            data = inputStream2ByteArray(remoteStorage + "/" + FILE_FULL_NAME);
            bis = new ByteArrayInputStream(data);
            int i = bis.available();
            byte[] bytes = new byte[i];
            bis.read(bytes);
            os = response.getOutputStream();
            os.write(bytes);
            os.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 确认上传
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void confirm() {
        List<DimAggData> dimAggDataList = dimExcelMgmtMapper.findDimAggs(null);
        Map<String, List<DimAggData>> dimAggMap = dimExcelMgmtMapper.findDimAggs(null)
                .stream()
                .collect(Collectors.groupingBy(DimAggData::getDimId));
        saveHandler(dimAggMap);
        List<String> dimIds = dimAggDataList.stream()
                .map(DimAggData::getDimId)
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(dimIds)) {
            dimExcelMgmtMapper.truncate();
        }
    }

    /**
     * 获取上传的标签列表
     *
     * @param pageNumber pageNumber
     * @param pageSize   pageSize
     * @param type       0为新增、1为更新
     * @return DimPage
     */
    @Override
    public DimPage<DimAggData> list(Integer pageNumber, Integer pageSize, String type, String dimName, String classify, String startTime, String endTime, String catalogId) {
        Page page = PageHelper.startPage(pageNumber, pageSize);
        List<DimAggData> list = dimAggMapper.list(type, dimName, classify, startTime, endTime,catalogId);
        List<DimDepartment> ddList = departmentMapper.list();
        Map<String,String> map = ddList.stream().collect(Collectors.toMap(DimDepartment::getId,e->e.getName()));
        for (DimAggData data:list) {
            data.setBelongName(map.get(data.getBelongTo()));
        }
        DimPage<DimAggData> dimDimPage = new DimPage<>();
        dimDimPage.setContent(list);
        dimDimPage.setTotalPages(page.getPages());
        dimDimPage.setTotalElements(page.getTotal());
        dimDimPage.setSize(page.getPageSize());
        dimDimPage.setRequestPager(pageNumber);
        return dimDimPage;
    }

    /**
     * 获取excel中的标签和库中的标签区别
     *
     * @param dimId dimId
     * @return DimCompareData
     * @throws IllegalAccessException IllegalAccessException
     */
    @Override
    public DimCompareData detail(String dimId) throws IllegalAccessException {
        Date date = null;
        DateFormat format1 = new SimpleDateFormat("yyyy-MM-dd");
        List<DimAggData> excelAggList = dimAggMapper.findDimAgg(dimId);
        DimCompareData result = new DimCompareData();
        DimAggData excelDim = excelAggList.get(0);
        try {//处理比较时间字段时的格式
            date = format1.parse(excelDim.getUptTime());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        excelDim.setUptTime(format1.format(date));
        excelDim.setClassify(ExcelDimDataModel.reversalClassifyMap.get(excelDim.getClassify()));
        excelDim.setUpdataCycle(ExcelDimDataModel.reversalUpdateCycleMap.get(excelDim.getUpdataCycle()));
        excelDim.setBusinessType(ExcelDimDataModel.reversalBusinessMap.get(excelDim.getBusinessType()));
        if (DIM_INSERT.equals(excelDim.getType())) {
            Map<String, String> excelMap = fetchField(excelDim);
            setInsert(excelMap, result);
            fetchItemMap(excelAggList, new ArrayList<>(), result);
        } else {
            List<DimAggData> dbAggList = dimAggMapper.findDimAggExist(dimId);
            DimAggData existDim = dbAggList.get(0);
            try {//处理比较时间字段时的格式
                date = format1.parse(existDim.getUptTime());
            } catch (ParseException e) {
                e.printStackTrace();
            }
            String dateString = format1.format(date);
            existDim.setUptTime(dateString);
            existDim.setClassify(ExcelDimDataModel.reversalClassifyMap.get(existDim.getClassify()));
            existDim.setUpdataCycle(ExcelDimDataModel.reversalUpdateCycleMap.get(existDim.getUpdataCycle()));
            existDim.setBusinessType(ExcelDimDataModel.reversalBusinessMap.get(existDim.getBusinessType()));
            DimCatalog secondCatalog = catalogMapper.findById(existDim.getCatalogId());
            DimCatalog firstCatalog = catalogMapper.findById(secondCatalog.getParentId());
            existDim.setSecondCatalog(secondCatalog.getName());
            if (null != firstCatalog) {
                existDim.setFirstCatalog(firstCatalog.getName());
            }
            Map<String, String> excelFieldMap = fetchField(excelDim);
            Map<String, String> existFieldMap = fetchField(existDim);
            fetchDifferent(excelFieldMap, existFieldMap, result);
            fetchItemMap(excelAggList, dbAggList, result);
        }
        return result;
    }

    /**
     * 批量替换
     *
     * @param dimIds dimIds
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized void batchReplace(String[] dimIds) {
        List<String> dimIdList = Arrays.asList(dimIds);
        Map<String, List<DimAggData>> dimAggMap = dimExcelMgmtMapper.findDimAggs(dimIdList)
                .stream()
                .collect(Collectors.groupingBy(DimAggData::getDimId));
        saveHandler(dimAggMap);
        dimExcelMgmtMapper.delDimAgg(dimIdList);
    }

    private void saveHandler(Map<String, List<DimAggData>> dimAggMap) {
        List<DimData> dimDataAddList = new ArrayList<>(dimAggMap.size());
        List<DimData> dimDataUpdateList = new ArrayList<>(dimAggMap.size());
        List<DimItem> dimItemAddList = new ArrayList<>(dimAggMap.size());
        List<String> itemDel = new ArrayList<>(dimAggMap.size());
        String current = DateFormatUtils.format(new Date(), "yyyy-MM-dd");
        dimAggMap.forEach((k, v) -> {
            DimAggData dimAggData = v.get(0);
            if (DIM_UPDATE.equals(dimAggData.getType())) {
                dimDataUpdateList.add(dimAggData.converToDimData(dimAggData));
                dimItemAddList.addAll(converToDimitem(v));
                itemDel.add(dimAggData.getDimId());
            } else {
                DimData dimData = dimAggData.converToDimData(dimAggData);
                dimData.setState(DimCofig.DIM_USEING);
                // 默认全局共享
                dimData.setSharedState("01");
                dimData.setCreateTime(current);
                dimDataAddList.add(dimData);
                dimItemAddList.addAll(converToDimitem(v));
                itemDel.add(dimAggData.getDimId());
            }
        });
        if (CollectionUtils.isNotEmpty(itemDel)) {
            // dimExcelMgmtMapper.delDimItems(itemDel);
            delDimItems(itemDel);
        }
        SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory()
                .openSession(ExecutorType.BATCH, false);
        DimMapper dimMapperBatch = sqlSession.getMapper(DimMapper.class);
        dimDataAddList.forEach(dimMapperBatch::add);
        dimDataUpdateList.forEach(dimMapperBatch::update);
        DimItemMapper dimItemMapper = sqlSession.getMapper(DimItemMapper.class);
        dimItemAddList.forEach(dimItem -> dimItemMapper.add(IdWorker.nextId(), dimItem.getDimId(), dimItem.getItemNo(), dimItem.getItemName(), dimItem.getUpdateDate()));
        try {
            sqlSession.commit();
            sqlSession.clearCache();
        } catch (Exception e) {
            sqlSession.rollback();
            throw new DimException(new ExceptionCode(70004, "导入数据发生异常"), e);
        } finally {
            sqlSession.close();
        }
    }

    private void delDimItems( List<String> dimIds ) {
        List<String> tmp = new ArrayList<>();
        for ( String dimId : dimIds ) {
            if (StringUtils.isNotBlank(dimId)) {
                tmp.add(dimId);
            }
            if ( tmp.size() >= 500 ) {
                dimExcelMgmtMapper.delDimItems(tmp);
                tmp = new ArrayList<>();
            }
        }
        if ( ( tmp != null ) && ( tmp.size() > 0 ) ) {
            dimExcelMgmtMapper.delDimItems(tmp);
        }
    }

    /**
     * 全部替换
     */
    @Override
    public void replaceAll() {
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void replaceByType(String type) {
        List<DimAggData> dimAggDataList = dimExcelMgmtMapper.findDimAggsByType(type);
        Map<String, List<DimAggData>> dimAggMap = dimExcelMgmtMapper.findDimAggsByType(type)
                .stream()
                .collect(Collectors.groupingBy(DimAggData::getDimId));
        saveHandler(dimAggMap);
        List<String> dimIds = dimAggDataList.stream()
                .map(DimAggData::getDimId).distinct()
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(dimIds)) {
            for (int i = 0; i < dimIds.size(); i += 1000) {
                int end = Math.min(i + 1000, dimIds.size());
                dimExcelMgmtMapper.delDimAgg(dimIds.subList(i, end));
            }
        }
    }

    @Override
    public List<ExcelCatalogModel> findSecondCatalog() {
        return dimAggMapper.findSecondCatalog();
    }

    private void setInsert(Map<String, String> excelMap, DimCompareData result) {
        Map<String, ContrastModel> contrastModelMap = new HashMap<>(excelMap.size());
        excelMap.forEach((k, v) -> {
            ContrastModel excelModel = new ContrastModel(v, true);
            contrastModelMap.put(k, excelModel);
        });
        result.setExcelMap(contrastModelMap);
    }

    private void fetchDifferent(Map<String, String> excelFieldMap, Map<String, String> existFieldMap, DimCompareData result) {
        Map<String, ContrastModel> excelMap = new HashMap<>(excelFieldMap.size());
        Map<String, ContrastModel> existsMap = new HashMap<>(existFieldMap.size());
        ContrastModel excelModel, existsModel;
        for (Map.Entry<String, String> entry : excelFieldMap.entrySet()) {
            if (entry.getValue()
                    .equals(existFieldMap.get(entry.getKey()))) {
                excelModel = new ContrastModel(entry.getValue(), true);
                putMap(excelMap, existsMap, excelModel, excelModel, entry.getKey());
            } else {
                excelModel = new ContrastModel(entry.getValue(), false);
                existsModel = new ContrastModel(existFieldMap.get(entry.getKey()), false);
                putMap(excelMap, existsMap, excelModel, existsModel, entry.getKey());
            }
        }
        result.setExcelMap(excelMap)
                .setExitsMap(existsMap);
    }

    private void putMap(Map<String, ContrastModel> excelMap, Map<String, ContrastModel> existsMap, ContrastModel excelModel, ContrastModel existsModel, String key) {
        if (!(ITEM_NAME.equals(key) || ITEM_NO.equals(key))) {
            excelMap.put(key, excelModel);
            existsMap.put(key, existsModel);
        }
    }

    private void fetchItemMap(List<DimAggData> excelAggList, List<DimAggData> dbAggList, DimCompareData result) {
        StringBuilder exitsItemNo = new StringBuilder();
        StringBuilder exitsItemName = new StringBuilder();
        StringBuilder excelItemNo = new StringBuilder();
        StringBuilder excelItemName = new StringBuilder();
        for (int i = 0; i < dbAggList.size(); i++) {
            appendValue(exitsItemNo, exitsItemName, dbAggList, i);
        }
        for (int i = 0; i < excelAggList.size(); i++) {
            appendValue(excelItemNo, excelItemName, excelAggList, i);
        }
        setItemValue(dbAggList, excelAggList, exitsItemNo.toString(), exitsItemName.toString(), excelItemNo.toString(), excelItemName.toString(), result.getExcelMap(), result.getExitsMap());
    }

    private void setItemValue(List<DimAggData> dbAggList, List<DimAggData> excelAggList, String exitsItemNo, String exitsItemName, String excelItemNo, String excelItemName, Map<String, ContrastModel> excelMap, Map<String, ContrastModel> exitsMap) {
        if (dbAggList.size() != excelAggList.size()) {
            exitsMap.put(ITEM_NAME, new ContrastModel(exitsItemName, false));
            exitsMap.put(ITEM_NO, new ContrastModel(exitsItemNo, false));
            excelMap.put(ITEM_NAME, new ContrastModel(excelItemName, false));
            excelMap.put(ITEM_NO, new ContrastModel(excelItemNo, false));
        } else {
            if (!exitsItemName.equals(excelItemName)) {
                exitsMap.put(ITEM_NAME, new ContrastModel(exitsItemName, false));
                exitsMap.put(ITEM_NO, new ContrastModel(exitsItemNo, false));
                excelMap.put(ITEM_NAME, new ContrastModel(excelItemName, false));
                excelMap.put(ITEM_NO, new ContrastModel(excelItemNo, false));
            } else if (!exitsItemNo.equals(excelItemNo)) {
                exitsMap.put(ITEM_NAME, new ContrastModel(exitsItemName, true));
                exitsMap.put(ITEM_NO, new ContrastModel(exitsItemNo, false));
                excelMap.put(ITEM_NAME, new ContrastModel(excelItemName, true));
                excelMap.put(ITEM_NO, new ContrastModel(excelItemNo, false));
            } else {
                exitsMap.put(ITEM_NAME, new ContrastModel(exitsItemName, true));
                exitsMap.put(ITEM_NO, new ContrastModel(exitsItemNo, true));
                excelMap.put(ITEM_NAME, new ContrastModel(excelItemName, true));
                excelMap.put(ITEM_NO, new ContrastModel(excelItemNo, true));
            }
        }
    }

    private void appendValue(StringBuilder itemNo, StringBuilder itemName, List<DimAggData> list, int index) {
        itemNo.append(list.get(index)
                .getItemNo());
        itemName.append(list.get(index)
                .getItemName());
        if (index != list.size() - 1) {
            itemNo.append(",");
            itemName.append(",");
        }
    }
}
